﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using PServiceBus.Core.Interface;
using PServiceBus.Core.Configuration;

namespace PServiceBus.Core.Runtime.Configuration {
    public static class ConfigurationProvider {
        static IConfiguration _configuration = null;
        static ConfigurationProvider() {
            RegisterAssemblySearchPaths();
            var configurationSetup = ConfigurationSetup;
            if (configurationSetup != null) {
                var type = configurationSetup.Type;
                var ex = MethodHelper.Try(() => 
                    _configuration = Phoenix.ReflectionExtensions.DynamicBroker.Broker[type].New(configurationSetup.FileName) as IConfiguration);
                MethodHelper.Try(() => PServiceBus.Core.Logger.ESBLogger.TraceError(ex.ToString()));
            }
        }

        internal static void RegisterAssemblySearchPaths() {
            var paths = new []{"MessageTransports","Topics","Loggers","MessageHandlers","Configurations",
                "TopicObservers","SubscriberObservers","SubscriptionManagers","WebSockets","."};
            Phoenix.ReflectionExtensions.DynamicBroker.SearchPaths.AddRange(paths);
        }

        internal static IConfiguration Configuration {
            get {
                return _configuration ?? (_configuration = new FileConfiguration());
            }
            set {
                _configuration = value;
            }
        }
        private static ConfigurationSetupSection ConfigurationSetup {
            get { return ConfigurationFactory.GetConfigSection<ConfigurationSetupSection>(); }
        }

        
        private static IServiceBusConfig _serviceBusConfig;
        public static IServiceBusConfig ServiceBusConfig {
            get {
                return _serviceBusConfig ?? (_serviceBusConfig = Configuration.ServiceBusConfig);
            }
            set { _serviceBusConfig = value; }
        }

        private static ILoggerConfig _loggerConfig;
        public static ILoggerConfig LoggerConfig {
            get {
                return _loggerConfig ?? (_loggerConfig = Configuration.LoggerConfig);
            }
            set { _loggerConfig = value; }
        }

        private static IComponentContainerConfig _componentContainerConfig;
        public static IComponentContainerConfig ComponentContainerConfig {
            get {
                return _componentContainerConfig ?? (_componentContainerConfig = Configuration.ComponentContainerConfig);
            }
            set { _componentContainerConfig = value; }
        }

        private static IComponentConfig _componentConfig;
        public static IComponentConfig ComponentConfig {
            get {
                return _componentConfig ?? ( _componentConfig = Configuration.ComponentConfig);
            }
            set { _componentConfig = value; }
        }

        private static IDistributorConfig _distributorConfig;
        public static IDistributorConfig DistributorConfig {
            get {
                return _distributorConfig ?? (_distributorConfig = Configuration.DistributorConfig);
            }
            set { _distributorConfig = value; }
        }

        private static IList<IMessageStoreConfig> _messageStoreConfigs;
        public static IList<Interface.IMessageStoreConfig> MessageStoreConfigs {
            get {
                return _messageStoreConfigs ?? (_messageStoreConfigs = Configuration.MessageStoreConfigs);
            }
            set { _messageStoreConfigs = value; }
        }

        private static IList<ISubscriptionManagerConfig> _subscriptionConfigs;
        public static IList<Interface.ISubscriptionManagerConfig> SubscriptionConfigs {
            get {
                return _subscriptionConfigs ?? (_subscriptionConfigs = Configuration.SubscriptionConfigs);
            }
            set { _subscriptionConfigs = value; }
        }

        private static ISecurityConfig _securityConfig;
        public static Interface.ISecurityConfig SecurityConfig {
            get {
                return _securityConfig ?? (_securityConfig = Configuration.SecurityConfig);
            }
            set { _securityConfig = value; }
        }

        private static IList<IObserverConfig> _observerConfigs;
        public static IList<Interface.IObserverConfig> ObserverConfigs {
            get {
                return _observerConfigs ?? (_observerConfigs = Configuration.ObserverConfigs);
            }
            set { _observerConfigs = value; }
        }

        private static IList<ITopicMessageEnricherConfig> _topicEnricherConfigs;
        public static IList<Interface.ITopicMessageEnricherConfig> TopicEnricherConfigs {
            get {
                return _topicEnricherConfigs ?? (_topicEnricherConfigs = Configuration.TopicEnricherConfigs);
            }
            set { _topicEnricherConfigs = value; }
        }

        private static IList<ITopicMessageInspectorConfig> _topicMessageInspectorConfigs;
        public static IList<Interface.ITopicMessageInspectorConfig> TopicMessageInspectorConfigs {
            get {
                return _topicMessageInspectorConfigs ?? (_topicMessageInspectorConfigs = Configuration.TopicMessageInspectorConfigs);
            }
            set { _topicMessageInspectorConfigs = value; }
        }

        private static IList<IWebStreamManagementConfig> _webStreamManagementConfigs;
        public static IList<Interface.IWebStreamManagementConfig> WebStreamManagementConfigs {
            get {
                return _webStreamManagementConfigs ?? (_webStreamManagementConfigs = Configuration.WebStreamManagementConfigs);
            }
            set { _webStreamManagementConfigs = value; }
        }

        private static Type _objectManager;
        /// <summary>
        /// Gets the current object manager type used for creating object manager connecting to RabbitMQ, MSMQ, e.t.c.
        /// </summary>
        /// <value>The object manager.</value>
        public static Type ObjectManager {
            get {
                if (_objectManager == null) {
                    var config = ServiceBusConfig;
                    if (config != null) {
                        var asm = Phoenix.ReflectionExtensions.AssemblyHelper.LoadAssembly(config.ObjectManagerType);
                        _objectManager = asm.GetTypes().FirstOrDefault(type => type.GetInterface(typeof(IObjectManager<>).FullName) != null);
                    }
                }
                return _objectManager;
            } 
        }

        private static Type _objectProviderType;
        /// <summary>
        /// Get the current object provider type used as the underlying object provider for use in an object manager
        /// </summary>
        /// <value>The object provider.</value>
        public static Type ObjectProviderType {
            get {
                if (_objectProviderType == null) {
                    var config = ServiceBusConfig;
                    if (config != null) {
                        var asm = Phoenix.ReflectionExtensions.AssemblyHelper.LoadAssembly(config.ObjectManagerType);
                        _objectProviderType = asm.GetTypes().FirstOrDefault(type => type.GetInterface(typeof(IObjectProvider<>).FullName) != null);
                    }
                }
                return _objectProviderType;
            }
        }

        private static Type _keyValueStoreType;
        /// <summary>
        /// Get the current key value store type used as the underlying key value store
        /// </summary>
        /// <value>The key value store.</value>
        public static Type KeyValueStoreType {
            get {
                if (_keyValueStoreType == null) {
                    var config = ServiceBusConfig;
                    if (config != null) {
                        var asm = Phoenix.ReflectionExtensions.AssemblyHelper.LoadAssembly(config.ObjectManagerType);
                        _keyValueStoreType = asm.GetTypes().FirstOrDefault(type => type.GetInterface(typeof(IKeyValueStore).FullName) != null);
                    }
                }
                return _keyValueStoreType;
            }
        }

        private static string _address;
        /// <summary>
        /// Gets the global address used by the service bus for subscription and e.t.c.
        /// </summary>
        /// <value>The queue path.</value>
        public static string Address { 
            get {
                if (_address == null) { 
                   var config = ServiceBusConfig;
                   if (config != null) _address = config.Address;
                }
                return _address;
            }
        }
        private static Type _esbLogger;
        /// <summary>
        /// Gets the current esb logger type use for logging error/information.
        /// </summary>
        /// <value>The esb logger.</value>
        public static Type ESBLogger {
            get {
                if (_esbLogger == null) {
                    var loggerConfig = LoggerConfig;
                    if (loggerConfig != null) {
                        var type = loggerConfig.Type;
                        var loggerType = Phoenix.ReflectionExtensions.DynamicBroker.Broker[type];
                        _esbLogger = loggerType.HasType ? loggerType.Type : Type.GetType(type, false);
                    }
                }
                return _esbLogger;
            }
        }
        private static int? _publishMessageRetryCount;
        /// <summary>
        /// Gets the amount of time to retry a message before moving it to a dead message queue/data store
        /// </summary>
        /// <value>The publish message retry count.</value>
        public static int PublishMessageRetryCount {
            get {
                if (_publishMessageRetryCount == null) {
                    var config = ServiceBusConfig;
                    if (config != null) _publishMessageRetryCount = config.MessageRetryCount;
                }
                return _publishMessageRetryCount ?? 5;
            }
        }
    }
}
